import threading
import time
import random

class SlidingWindow:
    def __init__(self, window_size=4):
        self.window_size = window_size
        self.sender_base = 0
        self.sender_next = 0
        self.receiver_base = 0
        self.is_running = True
        self.packet_loss_prob = 0.3
        self.stats_thread = None

    def sender(self):
        while self.is_running:
            # 发送窗口内的数据包
            while self.sender_next < self.sender_base + self.window_size:
                print(f"甲方发送数据包，序号：{self.sender_next}")
                # 模拟丢包
                if random.random() > self.packet_loss_prob:
                    threading.Thread(target=self.receiver, args=(self.sender_next,)).start()
                else:
                    print(f"甲方数据包{self.sender_next}丢失...")
                self.sender_next += 1
                time.sleep(0.5)
            # 等待确认以滑动窗口
            time.sleep(1)

    def receiver(self, seq_num):
        # 模拟接收并返回确认
        if seq_num >= self.receiver_base:
            print(f"乙方收到数据包，序号：{seq_num}，返回确认")
            # 模拟丢包
            if random.random() > self.packet_loss_prob:
                threading.Thread(target=self.sender_ack, args=(seq_num,)).start()
            else:
                print(f"乙方确认{seq_num}丢失...")
        else:
            print(f"乙方收到重复数据包{seq_num}，丢弃")

    def sender_ack(self, seq_num):
        print(f"甲方收到确认，序号：{seq_num}")
        # 滑动发送窗口
        if seq_num == self.sender_base:
            self.sender_base = seq_num + 1
            print(f"发送窗口滑动，新的base：{self.sender_base}")

    def stats(self):
        while self.is_running:
            # 计算各窗口部分
            sent_and_ack = self.sender_base
            sent_not_ack = self.sender_next - self.sender_base
            allowed_not_sent = max(0, self.window_size - sent_not_ack)
            not_allowed = max(0, self.sender_next + allowed_not_sent - self.window_size - self.sender_base)
            print(f"已发送并收到确认：{sent_and_ack} | 已发送但未收到确认：{sent_not_ack} | 允许发送但尚未发送：{allowed_not_sent} | 不允许发送：{not_allowed}")
            time.sleep(1)

    def start(self):
        threading.Thread(target=self.sender).start()
        self.stats_thread = threading.Thread(target=self.stats)
        self.stats_thread.start()
        # 运行一段时间后停止
        time.sleep(10)
        self.is_running = True
        self.stats_thread.join()

if __name__ == "__main__":
    sw = SlidingWindow(window_size=4)
    sw.start()